package com.infolab.aims.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.infolab.aims.common.PaginationHelper;
import com.infolab.aims.common.QueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.domain.Attendance;
import com.infolab.aims.domain.SystemUser;
import com.infolab.aims.service.intf.AttendanceService;

@Service
@Transactional
public class AttendanceServiceImpl implements AttendanceService {
	
	Log logger = LogFactory.getLog(getClass());
	@PersistenceContext
	private EntityManager em;
	
	public synchronized void createAttendance(Attendance attendance)
	{ 
		SearchCriteria searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("nric", attendance.getSystemUser().getNric());
		
		searchCriteria.addSearchCriteria("timeIn", new LocalDate().toDateTimeAtStartOfDay().toDate());
		Attendance curAttendance = findAttendance(searchCriteria);
		if(curAttendance!=null)
		{
			curAttendance.setDateModified(new Date());
			curAttendance.setTimeOut(new Date());
			em.merge(curAttendance);
			BeanUtils.copyProperties(curAttendance, attendance);
		}
		else
		{
			Attendance newAttendance = new Attendance();
			
			attendance.setModifiedBy(attendance.getCreatedBy());
			
			attendance.setDateCreated(new Date());
			
			attendance.setTimeIn(new Date());
			
			attendance.setDateModified(new Date());
			
			BeanUtils.copyProperties(attendance,newAttendance);
			
			em.persist(newAttendance);
		
		}
	}

	public void updateAttendance(Attendance attendance) {
		
//		if(attendance.getSection()==null || attendance.getSection().getSectionId()==null)
//			attendance.setSection(null);
		
		Attendance newAttendance = em.find(Attendance.class, attendance.getAttendanceId());
		
		attendance.setDateModified(new Date());
		
		BeanUtils.copyProperties(attendance,newAttendance);
		
		em.merge(newAttendance);
	}


	public Attendance getAttendance(Integer attendanceId) {
		// TODO Auto-generated method stub
		Attendance attendance = em.find(Attendance.class, attendanceId);
		return attendance; 
	}

	public Attendance findAttendance(SearchCriteria searchCriteria) {
		searchCriteria.setMaxRow(1);
		List<Attendance> attendances = findAttendances(searchCriteria);
		if(attendances!=null && attendances.size()>=1)
			return attendances.get(0);

		return null;
	}
	
	public List<Attendance> findAttendances(SearchCriteria searchCriteria) {
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Attendance> cq = cb.createQuery(Attendance.class);
		Root<Attendance> root = cq.from(Attendance.class);
		List<Predicate> criteria = new ArrayList<Predicate>();

		Join<Attendance,SystemUser> technician = root.join("systemUser", JoinType.INNER);;
		if (searchCriteria.getSearchCriteria("nric")!= null) {
			ParameterExpression<String> p = cb.parameter(String.class, "nric");	
			criteria.add(cb.equal(cb.upper(technician.<String>get("nric")), cb.upper( p)));
		}

		if (searchCriteria.getSearchCriteria("timeIn")!= null) {
			ParameterExpression<Date> p = cb.parameter(Date.class, "timeIn");
			criteria.add(cb.greaterThanOrEqualTo(root.<Date>get("timeIn"), p));
			
		}


		cq.select(root);

		if (criteria.size() == 0) {
		} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
		} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}

		if(searchCriteria.getSortType()!=null)
		{
			if(searchCriteria.isAscendingSort())
				cq.orderBy(cb.asc(root.get(searchCriteria.getSortName())));
			else
				cq.orderBy(cb.desc(root.get(searchCriteria.getSortName())));	
		}

		Query query = em.createQuery(cq);

		
		if (searchCriteria.getSearchCriteria("nric")!= null) {
			query.setParameter("nric", searchCriteria.getSearchCriteria("nric"));	
		}

		if (searchCriteria.getSearchCriteria("timeIn")!= null) {
			query.setParameter("timeIn", searchCriteria.getSearchCriteria("timeIn"));
		}
		

		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());


		List<Attendance> attendances  = query.getResultList();
		if(attendances==null || attendances.size()==0)
			return null;

		return attendances;
	}

	public SearchResult searchAttendance(
			SearchCriteria searchCriteria) {
		
		if(searchCriteria.getCurPage()==null)
			searchCriteria.setCurPage(1);
		
		AttendancePaginationHelper helper = new AttendancePaginationHelper();
		
		return helper.getSearchResultFromQueryHelper(em, searchCriteria);
	}
	
	public void deleteAttendance(Integer attendanceId) {
		
		Query query = em.createQuery("update Attendance set active=0 where attendanceId=:attendanceId");
		
		query.setParameter("attendanceId", attendanceId);
		
		query.executeUpdate();
		
	}
	
	public static class AttendancePaginationHelper extends PaginationHelper<Attendance> {

		@Override
		public String[] getValidColumnName() {
			return new String[]{"attendanceId","attendanceName","active","systemUser.personName","systemUser.userType","timeIn"};
		}
		
		@Override
		public String[] getValidSortName() {
			return new String[]{"attendanceId","timeIn","timeOut","systemUser.personName","modifiedBy","systemUser.userType","dateModified"};
		}

		@Override
		public String getDefaultSortName() {
			return "attendanceId";
		}
		
		@Override
		public QueryHelper getQueryHelper() {
			QueryHelper queryHelper = new QueryHelper();
			queryHelper.select("select attendance from Attendance attendance inner join attendance.systemUser systemUser");
			return queryHelper;
		}
	}

}
